/* 

你这个学期必须选修 numCourses 门课程，记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出，其中 prerequisites[i] = [ai, bi] ，表示如果要学习课程 ai 则 必须 先学习课程  bi 。

例如，先修课程对 [0, 1] 表示：想要学习课程 0 ，你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习？如果可以，返回 true ；否则，返回 false 。

 

示例 1：

输入：numCourses = 2, prerequisites = [[1,0]]
输出：true
解释：总共有 2 门课程。学习课程 1 之前，你需要完成课程 0 。这是可能的。
示例 2：

输入：numCourses = 2, prerequisites = [[1,0],[0,1]]
输出：false
解释：总共有 2 门课程。学习课程 1 之前，你需要先完成​课程 0 ；并且学习课程 0 之前，你还应先完成课程 1 。这是不可能的。
 

提示：

1 <= numCourses <= 105
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
prerequisites[i] 中的所有课程对 互不相同

来源：力扣（LeetCode）
链接：https://leetcode-cn.com/problems/course-schedule
著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。

*/

// 题解：https://leetcode-cn.com/problems/course-schedule/solution/bao-mu-shi-ti-jie-shou-ba-shou-da-tong-tuo-bu-pai-/
// 拓扑排序
let canFinish = (numCourses, prerequisites) => {
    let inDegree = new Array(numCourses).fill(0); // 入度数组
    let map = {}; // 邻接表
    for (let i = 0; i < prerequisites.length; i++) {
        inDegree[prerequisites[i][0]]++; // 求课的初始入度值
        if (map[prerequisites[i][1]]) { // 当前课已经存在于邻接表
            map[prerequisites[i][1]].push(prerequisites[i][0]); // 添加依赖它的后续课
        } else { // 当前课不存在于邻接表
            map[prerequisites[i][1]] = [prerequisites[i][0]];
        }
    }
    let queue = [];
    for (let i = 0; i < inDegree.length; i++) { // 所有入度为0的课入列
        if (inDegree[i] == 0) queue.push(i);
    }
    let count = 0;
    while (queue.length) {
        let selected = queue.shift(); // 当前选的课，出列
        count++; // 选课数+1
        let toEnQueue = map[selected]; // 获取这门课对应的后续课
        if (toEnQueue && toEnQueue.length) { // 确实有后续课
            for (let i = 0; i < toEnQueue.length; i++) {
                inDegree[toEnQueue[i]]--; // 依赖它的后续课的入度-1
                if (inDegree[toEnQueue[i]] == 0) { // 如果因此减为0，入列
                    queue.push(toEnQueue[i]);
                }
            }
        }
    }
    return count == numCourses; // 选了的课等于总课数，true，否则false
};